package com.expert.miniapp.service.impl;

import com.expert.miniapp.config.AIAnalysisConfigProperties;
import com.expert.miniapp.dao.DemandMapper;
import com.expert.miniapp.dao.InstrumentMapper;
import com.expert.miniapp.dao.LaboratoryMapper;
import com.expert.miniapp.dao.OtherResourceMapper;
import com.expert.miniapp.dao.Recommendation;
import com.expert.miniapp.dao.RecommendationItem;
import com.expert.miniapp.dao.RecommendationItemMapper;
import com.expert.miniapp.dao.RecommendationMapper;
import com.expert.miniapp.dao.ResearchExpertMapper;
import com.expert.miniapp.dao.TechAchievementMapper;
import com.expert.miniapp.dto.AnalysisReportDTO;
import com.expert.miniapp.service.AnalysisReportService;
import com.expert.miniapp.util.DifyClient;
import com.expert.miniapp.util.ResourceTypeUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * AnalysisReportServiceImpl - Service implementation that generates AI analysis reports.
 */
@Service
public class AnalysisReportServiceImpl implements AnalysisReportService {

    private static final Logger logger = LoggerFactory.getLogger(AnalysisReportServiceImpl.class);

    private static final Map<String, String> RESOURCE_TYPE_TABLE_MAP;
    private static final Map<String, String> RESOURCE_PREFIX_TABLE_MAP;

    static {
        Map<String, String> typeMap = new HashMap<>();
        typeMap.put("科研专家", "kyzj");
        typeMap.put("科技成果", "kjcg");
        typeMap.put("仪器设备", "yqsb");
        typeMap.put("实验基地", "syjd");
        typeMap.put("其他资源", "qtzy");
        RESOURCE_TYPE_TABLE_MAP = Collections.unmodifiableMap(typeMap);

        Map<String, String> prefixMap = new HashMap<>();
        prefixMap.put("KYZJ", "kyzj");
        prefixMap.put("KJCG", "kjcg");
        prefixMap.put("YQSB", "yqsb");
        prefixMap.put("SYJD", "syjd");
        prefixMap.put("QTZY", "qtzy");
        RESOURCE_PREFIX_TABLE_MAP = Collections.unmodifiableMap(prefixMap);
    }

    @Autowired
    private RecommendationItemMapper recommendationItemMapper;

    @Autowired
    private RecommendationMapper recommendationMapper;

    @Autowired
    private DemandMapper demandMapper;

    @Autowired
    private ResearchExpertMapper researchExpertMapper;

    @Autowired
    private TechAchievementMapper techAchievementMapper;

    @Autowired
    private InstrumentMapper instrumentMapper;

    @Autowired
    private LaboratoryMapper laboratoryMapper;

    @Autowired
    private OtherResourceMapper otherResourceMapper;

    @Autowired
    private AIAnalysisConfigProperties aiConfig;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public AnalysisReportDTO generateAnalysisReport(String itemId) {
        logger.info("Generating analysis report itemId={}", itemId);

        RecommendationItem item = recommendationItemMapper.findByItemId(itemId);
        if (item == null) {
            throw new IllegalArgumentException("Recommendation item not found: " + itemId);
        }

        // 总是重新调用AI生成最新报告（不返回缓存），确保落库内容来自AI接口

        Map<String, Object> resource = queryResourceByType(item.getResourceType(), item.getResourceId());
        if (resource.isEmpty()) {
            throw new IllegalArgumentException("Resource not found: " + item.getResourceId());
        }
        logger.info("Loaded resource type={}, id={}", item.getResourceType(), item.getResourceId());

        Recommendation recommendation = recommendationMapper.findById(item.getRecommendationId());
        if (recommendation == null) {
            throw new IllegalArgumentException("Recommendation not found: " + item.getRecommendationId());
        }

        Map<String, Object> demand = queryDemandAsMap(recommendation.getDemandId());
        if (demand.isEmpty()) {
            throw new IllegalArgumentException("Demand not found: " + recommendation.getDemandId());
        }
        // 为需求加入匹配原因，增强上下文信息
        if (StringUtils.hasText(item.getReason())) {
            demand.put("match_reason", item.getReason());
        }
        logger.info("Loaded demand demandId={}", recommendation.getDemandId());

        // 使用流式SSE请求直接获取报告文本；如失败直接抛错（不做本地兜底）
        String report = callAIApiStreaming(
                resource,
                demand,
                item.getResourceId()
        );
        // 清洗AI输出：移除除主资源以外的资源编号
        report = sanitizeReportText(report, item.getResourceId());
        logger.info("AI report length {}", report.length());

        int updated = recommendationItemMapper.updateAnalysisReport(itemId, report);
        if (updated == 0) {
            logger.warn("Failed to persist analysis report itemId={}", itemId);
        }

        AnalysisReportDTO result = AnalysisReportDTO.builder()
                .itemId(itemId)
                .analysisReport(report)
                .generatedAt(LocalDateTime.now())
                .build();

        logger.info("Analysis report generated itemId={}", itemId);
        return result;
    }

    @Override
    public AnalysisReportDTO getAnalysisReport(String itemId) {
        logger.info("Fetching analysis report itemId={}", itemId);

        RecommendationItem item = recommendationItemMapper.findByItemId(itemId);
        if (item == null) {
            throw new IllegalArgumentException("Recommendation item not found: " + itemId);
        }

        if (!StringUtils.hasText(item.getAnalysisReport())) {
            throw new IllegalStateException("Analysis report not generated yet: " + itemId);
        }

        return AnalysisReportDTO.fromExisting(item);
    }

    private Map<String, Object> queryResourceByType(String resourceType, String resourceId) {
        Optional<String> tableName = resolveTableName(resourceType, resourceId);
        if (tableName.isPresent()) {
            String table = tableName.get();
            try {
                Map<String, Object> data = jdbcTemplate.queryForMap(
                        "SELECT * FROM " + table + " WHERE id = ?", resourceId);
                Map<String, Object> linked = new LinkedHashMap<>(data);
                if (!linked.isEmpty()) {
                    return linked;
                }
                logger.warn("Table {} returned empty result for resource {}, falling back to mapper", table, resourceId);
            } catch (EmptyResultDataAccessException ex) {
                logger.warn("No record found in table {} for resource {}", table, resourceId);
            } catch (Exception ex) {
                logger.warn("Failed to query table {} for resource {}, fallback to mapper. Cause={}", table, resourceId, ex.getMessage());
            }
        }
        Object fallback = queryResourceViaMapper(resourceId);
        if (fallback == null) {
            return Collections.emptyMap();
        }
        return toPlainMap(fallback);
    }

    private Optional<String> resolveTableName(String resourceType, String resourceId) {
        if (StringUtils.hasText(resourceType)) {
            String table = RESOURCE_TYPE_TABLE_MAP.get(resourceType.trim());
            if (table != null) {
                return Optional.of(table);
            }
        }
        String prefix = ResourceTypeUtils.getTypePrefix(resourceId);
        return Optional.ofNullable(RESOURCE_PREFIX_TABLE_MAP.get(prefix));
    }

    private Object queryResourceViaMapper(String resourceId) {
        String prefix = ResourceTypeUtils.getTypePrefix(resourceId);
        switch (prefix) {
            case "KYZJ":
                return researchExpertMapper.findById(resourceId);
            case "KJCG":
                return techAchievementMapper.findById(resourceId);
            case "YQSB":
                return instrumentMapper.findById(resourceId);
            case "SYJD":
                return laboratoryMapper.findById(resourceId);
            case "QTZY":
                return otherResourceMapper.findById(resourceId);
            default:
                logger.warn("Unrecognized resource prefix: {}", prefix);
                return null;
        }
    }

    private Map<String, Object> queryDemandAsMap(String demandId) {
        try {
            Map<String, Object> data = jdbcTemplate.queryForMap(
                    "SELECT * FROM demand WHERE demand_id = ?", demandId);
            return new LinkedHashMap<>(data);
        } catch (EmptyResultDataAccessException ex) {
            logger.warn("Demand not found: {}", demandId);
        } catch (Exception ex) {
            logger.warn("Direct query on demand table failed, fallback to mapper. Cause={}", ex.getMessage());
        }
        return Optional.ofNullable(demandMapper.selectByPrimaryKey(demandId))
                .map(this::toPlainMap)
                .orElse(Collections.emptyMap());
    }

    @SuppressWarnings("unchecked")
    private Map<String, Object> toPlainMap(Object source) {
        if (source == null) {
            return Collections.emptyMap();
        }
        ObjectMapper mapper = objectMapper.copy();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        return mapper.convertValue(source, Map.class);
    }

    private String toPrettyJson(Map<String, Object> data) {
        ObjectMapper mapper = objectMapper.copy();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        try {
            return mapper.writeValueAsString(data);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("JSON serialization failed: " + e.getMessage(), e);
        }
    }

    /**
     * 以SSE流式调用AI接口（OpenAI格式），返回完整报告文本。
     */
    private String callAIApiStreaming(Map<String, Object> resourceData,
                                      Map<String, Object> demandData,
                                      String resourceId) {
        logger.info("调用分析报告AI（OpenAI流式） resourceId={}", resourceId);

        // 1) 构建content对象（三个字段）
        String resourceType = determineResourceType(resourceId);
        Map<String, String> demandFieldMap = getDemandFieldNameMap();
        String customerNeeds = formatMapToKeyValueText(demandData, demandFieldMap);
        Map<String, String> resourceFieldMap = getResourceFieldNameMap(resourceType);
        String references = formatMapToKeyValueText(resourceData, resourceFieldMap);

        Map<String, Object> content = new LinkedHashMap<>();
        content.put("resource_type", resourceType);
        content.put("customer_needs", customerNeeds);
        content.put("references", references);

        logger.debug("Content: resource_type={}, customer_needs_length={}, references_length={}",
                resourceType, customerNeeds.length(), references.length());

        // 2) 调用OpenAI格式API（streaming模式/SSE）
        try {
            logger.info("调用分析报告AI: url={}, model={}",
                    aiConfig.getUrl(), aiConfig.getModel());

            long startTime = System.currentTimeMillis();
            String result = DifyClient.callOpenAIFormatStreaming(
                    aiConfig.getUrl(),
                    aiConfig.getKey(),
                    content,                    // 作为message.content对象
                    aiConfig.getModel(),        // gpt-4o
                    aiConfig.getTimeout(),
                    chunk -> {
                        // 流式输出时的回调（可选，这里仅记录日志）
                        logger.debug("收到流式数据片段，长度: {}", chunk.length());
                    }
            );
            long elapsed = System.currentTimeMillis() - startTime;

            if (!StringUtils.hasText(result)) {
                throw new RuntimeException("流式输出返回内容为空");
            }

            logger.info("流式分析报告接收完成: 耗时{}ms, 内容长度={}", elapsed, result.length());
            return result.trim();

        } catch (Exception ex) {
            logger.error("流式AI调用失败: {}", ex.getMessage(), ex);
            throw new RuntimeException("Streaming AI API invocation failed: " + ex.getMessage(), ex);
        }
    }

    /**
     * 判断是否为本地兜底报告（需剔除并进行重新生成）。
     */
    private boolean isFallbackReport(String text) {
        if (!StringUtils.hasText(text)) return false;
        String t = text;
        return t.contains("本地兜底报告") ||
               t.contains("规则化生成") ||
               t.contains("外部AI不可用或响应为空") ||
               t.contains("本地生成的结构化报告");
    }

    /**
     * 检查报告中是否包含除主资源ID之外的其它资源编号。
     */
    private boolean containsOtherResourceIds(String text, String expectedResourceId) {
        if (!StringUtils.hasText(text)) return false;
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(KYZJ|KJCG|YQSB|SYJD|QTZY)\\d{10}");
        java.util.regex.Matcher matcher = pattern.matcher(text);
        java.util.Set<String> ids = new java.util.HashSet<>();
        while (matcher.find()) {
            ids.add(matcher.group());
        }
        if (ids.isEmpty()) return false;
        // 只允许主资源ID存在
        return ids.stream().anyMatch(id -> !id.equals(expectedResourceId));
    }

    /**
     * 将报告中的非主资源编号清洗为通用占位文本，避免出现多个资源ID。
     */
    private String sanitizeReportText(String text, String expectedResourceId) {
        if (!StringUtils.hasText(text)) return text;
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(KYZJ|KJCG|YQSB|SYJD|QTZY)\\d{10}");
        java.util.regex.Matcher matcher = pattern.matcher(text);
        String sanitized = text;
        while (matcher.find()) {
            String id = matcher.group();
            if (!id.equals(expectedResourceId)) {
                // 使用中文占位文本替换，保留语义但去除编号
                sanitized = sanitized.replace(id, "（已省略资源编号）");
            }
        }
        // 若未显式包含主资源ID，则在报告开头加入说明
        if (!sanitized.contains(expectedResourceId)) {
            sanitized = "【本报告针对资源ID: " + expectedResourceId + "】\n\n" + sanitized;
        }
        return sanitized;
    }

    /**
     * 将Map转为键值对文本格式
     * @param data 数据Map
     * @param fieldNameMap 字段名中文映射（可选）
     * @return 格式化后的文本，如："姓名: 何高奇\n职称: 教授\n..."
     */
    private String formatMapToKeyValueText(Map<String, Object> data, Map<String, String> fieldNameMap) {
        StringBuilder sb = new StringBuilder();

        for (Map.Entry<String, Object> entry : data.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();

            // 过滤无效值
            if (value == null) continue;
            String valueStr = value.toString().trim();
            if (valueStr.isEmpty() ||
                valueStr.equals("稍后再填") ||
                valueStr.equals("暂无信息") ||
                valueStr.equalsIgnoreCase("null") ||
                valueStr.equals("None")) {
                continue;
            }

            // 使用中文字段名（如果有映射）
            String displayName = fieldNameMap != null ?
                fieldNameMap.getOrDefault(key, key) : key;

            sb.append(displayName).append(": ").append(valueStr).append("\n");
        }

        return sb.toString().trim();
    }

    /**
     * 根据resourceId确定中文资源类型名称
     */
    private String determineResourceType(String resourceId) {
        String prefix = ResourceTypeUtils.getTypePrefix(resourceId);
        switch (prefix) {
            case "KYZJ": return "科研专家";
            case "KJCG": return "科技成果";
            case "YQSB": return "仪器设备";
            case "SYJD": return "实验基地";
            case "QTZY": return "其他资源";
            default: return "未知资源";
        }
    }

    /**
     * 需求字段中文名映射
     */
    private Map<String, String> getDemandFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("demand_id", "需求ID");
        map.put("user_id", "用户ID");
        map.put("demand_name", "需求名称");
        map.put("demand_status", "需求状态");
        map.put("created_at", "创建时间");
        map.put("submitted_at", "提交时间");
        map.put("research_objective", "研究目标");
        map.put("core_technical_challenges", "核心技术难点");
        map.put("application_scenario", "应用场景");
        map.put("expected_outcomes", "预期成果");
        map.put("current_technology_status", "当前技术现状");
        map.put("technology_field", "技术领域");
        map.put("current_research_stage", "当前研究阶段");
        map.put("document_upload", "文档上传");
        map.put("cooperation_form", "合作形式");
        map.put("budget_range", "预算范围");
        map.put("timeline_requirement", "时间要求");
        map.put("regional_requirement", "地域要求");
        map.put("intellectual_property", "知识产权");
        map.put("urgency_level", "紧急程度");
        map.put("expected_contact_time", "期望联系时间");
        map.put("confidentiality_requirement", "保密要求");
        return map;
    }

    /**
     * 科研专家字段中文名映射
     */
    private Map<String, String> getKYZJFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("id", "专家ID");
        map.put("name", "姓名");
        map.put("title", "职称");
        map.put("university", "单位");
        map.put("college", "学院");
        map.put("education", "教育经历");
        map.put("work_experience", "工作经历");
        map.put("profile", "个人简介");
        map.put("social_positions", "社会职务");
        map.put("research_direction", "研究方向");
        map.put("research_projects", "科研项目");
        map.put("papers", "论文成果");
        map.put("awards", "获奖情况");
        map.put("talent_programs", "人才计划");
        map.put("patents", "专利");
        map.put("software_copyrights", "软件著作权");
        map.put("journals_books", "期刊书籍");
        map.put("participated_projects", "参与项目");
        map.put("academic_conferences", "学术会议");
        map.put("student_theses", "学生论文");
        map.put("other_info", "其他信息");
        return map;
    }

    /**
     * 科技成果字段中文名映射
     */
    private Map<String, String> getKJCGFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("id", "成果ID");
        map.put("name", "成果名称");
        map.put("application_scenarios", "应用场景");
        map.put("details", "详细信息");
        map.put("business_model", "商业模式");
        map.put("core_competitiveness", "核心竞争力");
        map.put("technical_features", "技术特点");
        map.put("categories", "分类");
        map.put("tags", "标签");
        map.put("organization", "所属机构");
        map.put("city", "城市");
        return map;
    }

    /**
     * 仪器设备字段中文名映射
     */
    private Map<String, String> getYQSBFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("id", "设备ID");
        map.put("name", "设备名称");
        map.put("organization", "所属机构");
        map.put("city", "城市");
        map.put("technical_parameters", "技术参数");
        map.put("function_introduction", "功能介绍");
        map.put("usage_instructions", "使用说明");
        map.put("fee_standard", "收费标准");
        map.put("appointment_instructions", "预约说明");
        map.put("categories", "分类");
        map.put("tags", "标签");
        return map;
    }

    /**
     * 实验基地字段中文名映射
     */
    private Map<String, String> getSYJDFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("id", "基地ID");
        map.put("name", "基地名称");
        map.put("research_areas", "研究领域");
        map.put("contributions", "贡献");
        map.put("categories", "分类");
        map.put("tags", "标签");
        map.put("organization", "所属机构");
        map.put("city", "城市");
        map.put("introduction", "简介");
        map.put("display_weight", "显示权重");
        return map;
    }

    /**
     * 其他资源字段中文名映射
     */
    private Map<String, String> getQTZYFieldNameMap() {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("id", "资源ID");
        map.put("name", "资源名称");
        map.put("introduction", "简介");
        map.put("organization", "所属机构");
        map.put("city", "城市");
        map.put("summary", "摘要");
        map.put("display_weight", "显示权重");
        return map;
    }

    /**
     * 根据资源类型获取字段名映射
     */
    private Map<String, String> getResourceFieldNameMap(String resourceType) {
        switch (resourceType) {
            case "科研专家": return getKYZJFieldNameMap();
            case "科技成果": return getKJCGFieldNameMap();
            case "仪器设备": return getYQSBFieldNameMap();
            case "实验基地": return getSYJDFieldNameMap();
            case "其他资源": return getQTZYFieldNameMap();
            default: return new HashMap<>();
        }
    }

}

